<?php

class Status_Object_Timeline {

  //Type of timeline, can be: user or home
  protected $_timelineType;

	//Identifier of the timeline usually userId
  protected $_timelineId;

 
  
  /* allowed types of timeline */
  static protected $_allowedTimelineTypes = array('user','home','mention','tag');

  /* allowed mode for user follower/following */
  static protected $_allowedFollowTypes = array('following','follower');
  
  /* mapping with db timeline_type */
  static protected $_timelineTypesDbMapping = array('user' => 'u','home' =>'h','mention' => 'm','tag' => 't');

  /* allowed mode for listingStreamPerStatusId */
  static protected $_allowedListingStreamPerStatusIdModes = array('before','after');
  
  /* 
	definition of the timeline stream table 
	This table contains all the statuses of the different timelines
  */
  static protected $_dbTimeLineStreamTableName = 'stream';
  //Timeline concatenated id (type.id )
  static protected $_dbTimeLineStreamTimelineIdCol = 'timelineCId';
  static protected $_dbTimeLineStreamStatusIdCol = 'statusId';
  static protected $_dbTimeLineStreamprivateCol = 'private';

  /* 
	definition of the timeline links table 
	this table contains the links between users, (who is following who)
  */
  static protected $_dbTimeLineLinksTableName = 'links';
  static protected $_dbTimeLineLinksTypeCol = 'type';
  static protected $_dbTimeLineLinkstimelineIdCol = 'timelineId';
  static protected $_dbTimeLineLinksUserIdCol = 'userId';
  
  
  
  
  /* need to put a limit to the number of pages that can be retrieved */
  static protected $_limitPage = 1000;
  
  
     /*
	__construct - public constructor of the class. It is defined by the type of timeline that is requested and the identifier of the timeline
	@param timelineType - set that can be: user or following
	@param id - identifier of the timeline, it is a digit that can be: userId
	*/
  public function __construct($timelineType,$id) {

 if(!in_array($timelineType,self::$_allowedTimelineTypes)) {
	  	require_once('status/objects/Exception.php');
  	    throw new Status_Object_Exception('Invalid Timeline Type',15);
	}
	
	if($timelineType == 'tag') {
		if(!self::_isValidTag($id)) {
			require_once('status/objects/Exception.php');
			throw new Status_Object_Exception('Invalid Tag: must be alphanumric and _ and less than 15 characters',15);				
		}
		//we change the identifier to lowercase
		$id = strtolower($id);
		
	} else {
		$validator = new Zend_Validate_Digits();
		if(!$validator->isValid($id)) {
			require_once('status/objects/Exception.php');
			throw new Status_Object_Exception('Invalid Identifier ',15);	
		}
	} //end else
	
	$this->_timelineType = $timelineType;
	$this->_timelineId = $id;

  }	//end constructor
  
    //The usual getter
  public function getTimelineType() {
	  return $this->_timelineType;	
  }
  public function getTimelineId() {
	  return $this->_timelineId;	
  }
  protected function _getTimelineCId() {
	return self::_getTimelineCIdStatic($this->getTimelineType(),$this->getTimelineId());
  }  
  
  //We need a static version for the publish status
  static protected function _getTimelineCIdStatic($timelineType,$timelineId) {
     return self::$_timelineTypesDbMapping[$timelineType].$timelineId; 
  }

  //delete - delete the status from the timelines databases
  //@param void
  //@return int number of appearance of the status that have been deleted on  the databases
  static public function deleteStatus($oStatus) {
  	if(!($oStatus instanceof Status_Object_Status)) {
  		require_once('status/objects/Exception.php');
  		throw new Status_Object_Exception('invalid status',60);
  	}
    $userId = (int) Zend_Registry::get('app_user')->getId();
	if($oStatus->getUserId() != $userId) {
  		require_once('status/objects/Exception.php');
  		throw new Status_Object_Exception('user can\'t delete status',15);		
	}
	
	//we retrieve all the timeline databases and delete the status in them
  	$dbs = self::_getAllStreamDbs();
  	$statusId = $oStatus->getId();
  	$nbPresence = 0;
  	foreach($dbs as $num => $db) {
  		$n = $db->delete(self::$_dbTimeLineStreamTableName,$db->quoteInto(self::$_dbTimeLineStreamStatusIdCol.' = ?',$statusId));	
  		$nbPresence += $n;
  	}
  	return $nbPresence;
  	
  }
 
  protected function _canViewPrivateStatuses() {

	$userId = Zend_Registry::get('app_user')->getId();

	//user can see the private statuses if he/she own the stream
	if($this->getTimelineId() == $userId) return true;
	return false;
	
	

  } // end _canViewPrivateStatuses()
  
  //This function publish the status to the different streams  of the user (user stream and home stream)
  
  static public function publishStatusToUser($oStatus) {


	//First we publish the status in the user list
	$userId = $oStatus->getUserId();
	$timelineCId = self::_getTimelineCIdStatic('user',$userId);

	$db = self::_getStreamDbStatic($timelineCId);
	
	$data = array(
		self::$_dbTimeLineStreamTimelineIdCol => $timelineCId,
		self::$_dbTimeLineStreamStatusIdCol => $oStatus->getId(),
		self::$_dbTimeLineStreamprivateCol => $oStatus->isPrivate(),
	);

Zend_Registry::get('dt_log')->log('publishing status to the user list:'.serialize($data), Zend_Log::DEBUG);
	
	$db->insert(self::$_dbTimeLineStreamTableName,$data);
	
	//second we publish to the user home stream (may not be the same db)
	$timelineCId = self::_getTimelineCIdStatic('home',$userId);

	$db = self::_getStreamDbStatic($timelineCId);
	
	$data = array(
		self::$_dbTimeLineStreamTimelineIdCol => $timelineCId,
		self::$_dbTimeLineStreamStatusIdCol => $oStatus->getId(),
		self::$_dbTimeLineStreamprivateCol => $oStatus->isPrivate(),
	);

Zend_Registry::get('dt_log')->log('publishing status to the user home:'.serialize($data), Zend_Log::DEBUG);
	
	$db->insert(self::$_dbTimeLineStreamTableName,$data);


  }

  
  static public function publishStatusToFollowers($oStatus) {

	//If the status is private, it is not published to followers
	if($oStatus->isPrivate()) return;

	//We search the list of people following the user
	$userId = $oStatus->getUserId();

	$list = self::getFollowersList($userId);
	Zend_Registry::get('dt_log')->log('Retrieved list of followers:'.serialize($list), Zend_Log::DEBUG);
	
	if(empty($list)) return;
		
	//different users may not be on the same database, we need to search the database for each of them
	$dbData = array();
	//No need to call another object each time
	$private = $oStatus->isPrivate();
	$statusId = $oStatus->getId();
	
	foreach($list as $num => $userId) {
		//we look for the CID of the user to publish in his home
		$timelineCId = 	self::_getTimelineCIdStatic('home',$userId);
		
		//we gather them together per database
		//Identifier of the database is given by _getStreamDbNumStatic	
		$dbData[self::_getStreamDbNumStatic($timelineCId)][] = array(
			self::$_dbTimeLineStreamTimelineIdCol => $timelineCId,
			self::$_dbTimeLineStreamStatusIdCol => $statusId,
			self::$_dbTimeLineStreamprivateCol => $private,
		);
	}
	Zend_Registry::get('dt_log')->log('number of databases to publish to:'.count($dbData), Zend_Log::DEBUG);	
	//push the update per database in one shoot
	foreach($dbData as $num => $dataUsers) {
		//we retrieve the db details based on first user CID
		$db = self::_getStreamDbStatic($dataUsers[0][self::$_dbTimeLineStreamTimelineIdCol]);
		//we publish the data
		foreach($dataUsers as $num => $row)	{
			$db->insert(self::$_dbTimeLineStreamTableName,$row);
		} //end insertion each row
	} //end insertation in one db

  } //end publishStatusToFollowers



	static public function publishStatusToMentions($oStatus) {

		//basic data check
		if(!($oStatus instanceof Status_Object_Status)) {
  			require_once('status/objects/Exception.php');
  			throw new Status_Object_Exception('invalid status',60);
  		}

		//We retrieve the list of mentions
		$mentions = $oStatus->getMentions();
		if(empty($mentions)) return;
		
		foreach($mentions as $num => $user) {
		

			
			//foreach mention, we publish the status to the mention stream
			$timelineCId = 	self::_getTimelineCIdStatic('mention',$user['id']);
			
			//we complete teh row details	
			$row = array(
				self::$_dbTimeLineStreamTimelineIdCol => $timelineCId,
				self::$_dbTimeLineStreamStatusIdCol => $oStatus->getId(),
				self::$_dbTimeLineStreamprivateCol => $oStatus->isPrivate()
			);
			$db = self::_getStreamDbStatic($timelineCId);		
			$db->insert(self::$_dbTimeLineStreamTableName,$row);
			
			//If the user is not following the author of the status, we publish the mention in the home
			//other case is when the message is private, it is not published to followers but it must be still published in the home
			//else it is already published using the follower publishing functionality
			if(!self::isFollowingById($user['id'], $oStatus->getUserId()) || $oStatus->isPrivate() ) {
			
				//if the user is mentioning himself no need to publish
				$statusUserId = $oStatus->getUserId();
				if($user['id'] == $statusUserId) continue;
			
				$timelineCId = 	self::_getTimelineCIdStatic('home',$user['id']);
				
				//we complete teh row details	
				$row = array(
					self::$_dbTimeLineStreamTimelineIdCol => $timelineCId,
					self::$_dbTimeLineStreamStatusIdCol => $oStatus->getId(),
					self::$_dbTimeLineStreamprivateCol => $oStatus->isPrivate()
				);					
				$db = self::_getStreamDbStatic($timelineCId);		
				$db->insert(self::$_dbTimeLineStreamTableName,$row);
			} //end user is not following status owner
		
		} //end foreach mention

	} //publishStatusToMentions

	
  //This function publish the status to the different tags streams
  //Only public statuses are published to the stream
  
  static public function publishStatusToTags($oStatus) {

	//basic data check
	if(!($oStatus instanceof Status_Object_Status)) {
		require_once('status/objects/Exception.php');
		throw new Status_Object_Exception('invalid status',60);
	}
	
	$isPrivate = $oStatus->isPrivate();
	if($isPrivate) return;

	//We retrieve the list of tags
	$tags = $oStatus->getTags();
	if(empty($tags)) return;
	

	foreach($tags as $num => $tag) {
		//First we publish the status in the user list
		$userId = $oStatus->getUserId();
		$timelineCId = self::_getTimelineCIdStatic('tag',$tag);

		$db = self::_getStreamDbStatic($timelineCId);
		
		$data = array(
			self::$_dbTimeLineStreamTimelineIdCol => $timelineCId,
			self::$_dbTimeLineStreamStatusIdCol => $oStatus->getId(),
			self::$_dbTimeLineStreamprivateCol => $isPrivate,
		);

	Zend_Registry::get('dt_log')->log('publishing status to the tag list:'.serialize($data), Zend_Log::DEBUG);
		
		$db->insert(self::$_dbTimeLineStreamTableName,$data);
	} //end foreach tag	


  } //end publishStatusToTags
  
  

 
  
	
	
	
  
  //Object model when returned in the API
  public function asArray() {

  }
  


  static public function formatPage($page = 1) {
      if(empty($page)) $page = 1;
      $validator = new Zend_Validate_Digits();
      if(!$validator->isValid($page)) {
  		require_once('status/objects/Exception.php');
  		throw new Status_Object_Exception('invalid input parameters',15);      	
  	  }
  	  if(self::$_limitPage < $page) {
  		require_once('status/objects/Exception.php');
  		throw new Status_Object_Exception('page search is limited to '.self::$_limitPage.' pages',15);   	  	
  	  }
  	  
  	  if($page < 1 ) $page = 1;
  	  return $page;		
  }

  static public function formatPerPage($perPage = 20) {
      if(empty($perPage)) $perPage = 20;
      $validator = new Zend_Validate_Digits();
      if(!$validator->isValid($perPage)) {
  		require_once('status/objects/Exception.php');
  		throw new Status_Object_Exception('invalid input parameters',15);      	
  	  }
  	  if($perPage < 1 ) $perPage = 1;
  	  if($perPage > 100 ) $perPage = 100;
  	  return $perPage;
  }
  
  
  
  //////////////////////listing string functionalities /////////////////////////

  /* listingStream() - used to retrieve the latest statuses on a particular stream
	deprecated - listingStreamPerPage or listingStreamPerId should be used
  	@param perPage - number of statuses to return
  	@param page - num of the page to return
  	@return array of php statuses	 
  */
	
  public function listingStream($perPage = 20,$page = 1) {
   
Zend_Registry::get('dt_log')->log('per page:'.$perPage.'-'.isset($perPage).'-'.empty($perPage), Zend_Log::DEBUG);	       
      //We validate the inputs
 	  $page = self::formatPage($page);
 	  $perPage = self::formatPerPage($perPage);
 	  
  	  $db = $this->_getStreamDb();
	  
	  //select from database
	  $select = $db->select()->from(self::$_dbTimeLineStreamTableName)->limitPage($page, $perPage);
      //we select a particular stream
	  $select->where($db->quoteInto(self::$_dbTimeLineStreamTimelineIdCol ." = ?", $this->_getTimelineCId()));
      
	  //we had a condition if the user is not allowed to view private statuses
	  if(!$this->_canViewPrivateStatuses()) {
		$select->where($db->quoteInto(self::$_dbTimeLineStreamprivateCol ." = ?", false));	  
	  }
	  
	  //we order by status id with more recent statuses first
	  $select->order(self::$_dbTimeLineStreamStatusIdCol.' DESC');	

      $res = $db->fetchAll($select->__toString());    	  

      if(empty($res)) return array();

      //We must reinterpret the rows so it is independant to the way it is stored
      $results = array();
      require_once('status/objects/status.php');
	  foreach($res as $num => $row) {

	Zend_Registry::get('dt_log')->log($row, Zend_Log::DEBUG);
	
		try{
		$status = new Status_Object_Status($row[self::$_dbTimeLineStreamStatusIdCol]);
      	$results[] = $status->asArray();
		}catch(Status_Object_Exception $e) {
			Zend_Registry::get('dt_log')->log('Status: Issue status not found on db:'.$row[self::$_dbTimeLineStreamStatusIdCol], Zend_Log::ERR);
		}
		
      }
    
      return $results;


  } //end listingStream

  
  
  
  
  
    /* listingStreamPerPage() - used to retrieve the latest statuses on a particular stream on a page format
  	@param perPage - number of statuses to return
  	@param page - num of the page to return
  	@return array of php statuses	 
  */
	
  public function listingStreamPerPage($perPage = 20,$page = 1) {
   
Zend_Registry::get('dt_log')->log('per page:'.$perPage.'-'.isset($perPage).'-'.empty($perPage), Zend_Log::DEBUG);	       
      //We validate the inputs
 	  $page = self::formatPage($page);
 	  $perPage = self::formatPerPage($perPage);
 	  
  	  $db = $this->_getStreamDb();
	  
	  //select from database
	  $select = $db->select()->from(self::$_dbTimeLineStreamTableName)->limitPage($page, $perPage);
      //we select a particular stream
	  $select->where($db->quoteInto(self::$_dbTimeLineStreamTimelineIdCol ." = ?", $this->_getTimelineCId()));
      
	  //we had a condition if the user is not allowed to view private statuses
	  if(!$this->_canViewPrivateStatuses()) {
		$select->where($db->quoteInto(self::$_dbTimeLineStreamprivateCol ." = ?", false));	  
	  }
	  
	  //we order by status id with more recent statuses first
	  $select->order(self::$_dbTimeLineStreamStatusIdCol.' DESC');	
	  
	  //We call the generic function to generate the stream
	  return $this->_listingStreamSelect($select);

  } //end listingStream
  
 


     /* listingStreamPerPage() - used to retrieve the latest statuses on a particular stream on a page format
  	@param perPage - number of statuses to return
  	@param page - num of the page to return
  	@return array of php statuses	 
  */
	
  public function listingStreamPerStatusId($mode,$statusId,$perPage = 20) {

    //We validate the inputs
 	$perPage = self::formatPerPage($perPage);
	  
	$validator = new Zend_Validate_Digits();
	if(!$validator->isValid($statusId)) {
	  	require_once('status/objects/Exception.php');
  	    throw new Status_Object_Exception('Invalid Status Identifier',15);	
	}
	
	if(!in_array($mode,self::$_allowedListingStreamPerStatusIdModes)) {
	  	require_once('status/objects/Exception.php');
  	    throw new Status_Object_Exception('Invalid mode',19);		  
	}
	
Zend_Registry::get('dt_log')->log('listing stream mode:'.$mode.'-'.$statusId.'-'.$perPage, Zend_Log::DEBUG);	       

 	  
  	  $db = $this->_getStreamDb();
	  
	  //select from database
	  $select = $db->select()->from(self::$_dbTimeLineStreamTableName)->limit($perPage);
      //we select a particular stream
	  $select->where($db->quoteInto(self::$_dbTimeLineStreamTimelineIdCol ." = ?", $this->_getTimelineCId()));

      //we gave the id limiter (this depends on mode)
	if('after' == $mode) {
		$select->where($db->quoteInto(self::$_dbTimeLineStreamStatusIdCol ." > ?", $statusId));
		$select->order(self::$_dbTimeLineStreamStatusIdCol.' ASC');
		//Do we have to invert the results
		//tricks because if we don t invert we have the earliest statuses first
		$invert = true;
	} elseif('before' == $mode) {
		$select->where($db->quoteInto(self::$_dbTimeLineStreamStatusIdCol ." < ?", $statusId));	  
		$select->order(self::$_dbTimeLineStreamStatusIdCol.' DESC');
		//tricks because if we don t invert we have the earliest statuses first
		$invert = false;
	}
	  
	  //we had a condition if the user is not allowed to view private statuses
	  if(!$this->_canViewPrivateStatuses()) {
		$select->where($db->quoteInto(self::$_dbTimeLineStreamprivateCol ." = ?", false));	  
	  }
	  
	  //we order by status id with more recent statuses first
	
	  
	  //We call the generic function to generate the stream
	  return $this->_listingStreamSelect($select,$invert);

  } //end listingStream
 

 
  
   /* _listingStreamSelect() - used to retrieve the latest statuses on a particular stream on a page format
  	@param select 
	@param invert for searching statuses after the givn one we are obliged to invert the results to have the latest first (db trick)
	@return array of php statuses, not status objects but status asArray
  */ 
  protected function _listingStreamSelect($select,$invert = false) {
  	$db = $this->_getStreamDb();
	$res = $db->fetchAll($select->__toString());    	  

    if(empty($res)) return array();

	//We must reinterpret the rows so it is independant to the way it is stored
	$results = array();
	require_once('status/objects/status.php');
	

	
	foreach($res as $num => $row) {
		try{
			$status = new Status_Object_Status($row[self::$_dbTimeLineStreamStatusIdCol]);
			$results[] = $status->asArray();
		} catch(Status_Object_Exception $e) {
			Zend_Registry::get('dt_log')->log('Status: Issue status not found on db:'.$row[self::$_dbTimeLineStreamStatusIdCol], Zend_Log::ERR);
		}
		
	}
	
	if($invert) krsort($results);
	
	return $results; 
  }
  
  
  
  
  
   ////////////////////// end listing string functionalities ///////////////////////// 
  
  
  
  
  
  
  
  //followersList function returns a list of followers of a particular person
  static public function getFollowersList($userId) {

	$validator = new Zend_Validate_Digits();
    if(!$validator->isValid($userId)) {
	  	require_once('status/objects/Exception.php');
  	    throw new Status_Object_Exception('Invalid Identifier',15);	
	}


      $db = self::_getConnexionDbStatic();
  	
	  //select from database
	  //follower is stored in the db the following way u - userId - followerId
	  
	  $select = $db->select()->from(self::$_dbTimeLineLinksTableName,self::$_dbTimeLineLinksUserIdCol);
      //we select a particular stream
	  $select->where($db->quoteInto(self::$_dbTimeLineLinksTypeCol ." = ?", self::$_timelineTypesDbMapping['user']));
	  $select->where($db->quoteInto(self::$_dbTimeLineLinkstimelineIdCol ." = ?", $userId));
	  
      $res = $db->fetchAll($select->__toString());    	  

      if(empty($res)) return array();	  	    	
	  
	  $list = array();
	  foreach($res as $num => $row) {
	  	$list[] = (int) $row[self::$_dbTimeLineLinksUserIdCol];
	  }
	  
	  return $list;
  	
  }

    //following function returns a list of users a person is following
  static public function getFollowingList($userId) {

	$validator = new Zend_Validate_Digits();
    if(!$validator->isValid($userId)) {
	  	require_once('status/objects/Exception.php');
  	    throw new Status_Object_Exception('Invalid Identifier',15);	
	}


      $db = self::_getConnexionDbStatic();
  	
	  //select from database
	  //follower is stored in the db the following way u - userId - followerId
	  
	  $select = $db->select()->from(self::$_dbTimeLineLinksTableName,self::$_dbTimeLineLinkstimelineIdCol);
      //we select a particular stream
	  $select->where($db->quoteInto(self::$_dbTimeLineLinksTypeCol ." = ?", self::$_timelineTypesDbMapping['user']));
	  $select->where($db->quoteInto(self::$_dbTimeLineLinksUserIdCol ." = ?", $userId));
      $res = $db->fetchAll($select->__toString());    	  

      if(empty($res)) return array();	  	    	
	  
	  $list = array();
	  foreach($res as $num => $row) {
	  	$list[] = (int) $row[self::$_dbTimeLineLinkstimelineIdCol];
	  }
	  
	  return $list;
  	
  }

	//isFollowing checks if the user given as first parameter is following user given as second parameter
	static public function isFollowing($oUser, $oUserFollowed) {
	  //Check we have user obejcts as input
	  if(!($oUser instanceof Myzen_User) || !($oUserFollowed instanceof Myzen_User)) {
	  	require_once('status/objects/Exception.php');	  
  	    throw new Status_Object_Exception('User not found',15);	
	  }
	
	
      return self::isFollowingById($oUser->getId(), $oUserFollowed->getId()); 
  }


	//isFollowingById checks if the userId given as first parameter is following userId given as second parameter
	static public function isFollowingById($userId, $userFollowedId) {
	
	
      $db = self::_getConnexionDbStatic();
  	
	  //select from database
	  //follower is stored in the db the following way u - userId - followerId
	  
	  $select = $db->select()->from(self::$_dbTimeLineLinksTableName,self::$_dbTimeLineLinkstimelineIdCol);
      //we select a particular stream
	  $select->where($db->quoteInto(self::$_dbTimeLineLinksTypeCol ." = ?", self::$_timelineTypesDbMapping['user']));
	  $select->where($db->quoteInto(self::$_dbTimeLineLinksUserIdCol ." = ?", $userId));
	  $select->where($db->quoteInto(self::$_dbTimeLineLinkstimelineIdCol ." = ?", $userFollowedId));
	  
      $res = $db->fetchAll($select->__toString());    	  

      if(empty($res)) return false;	  	    	
	  return true; 
  }

  
  /* create a friendship between the authenticated author and the provided userId */
  static public function createFriendship($userId) {
	//We validate the input
	$validator = new Zend_Validate_Digits();
    if(!$validator->isValid($userId)) {
	  	require_once('status/objects/Exception.php');
  	    throw new Status_Object_Exception('Invalid Identifier',15);	
	}
	
	//We retrieve the authenticated user
	$user = Zend_Registry::get('app_user'); 
	
	//we retrieve the user the authenticated user wants to follow
	require_once('Myzen/User.php');

	try{
		$userToFollow = new Myzen_User(array('id' => $userId));
	} catch(Zend_Auth_Exception $e) {
		//We convert the zend_auth_exception into a Status_Object_Exception
	  	require_once('status/objects/Exception.php'); 
		throw new Status_Object_Exception($e->getMessage(),$e->getCode());			
	}

	//We check that it is not the same person (can t follow itself)
	if($userToFollow->getId() == $user->getId()) {
	  	require_once('status/objects/Exception.php');  
		throw new Status_Object_Exception('user can\'t follow him/herself',28);		
	}
	//We check that  the authenticated user isn t already following the user
	if(self::isFollowing($user, $userToFollow)) {
		require_once('status/objects/Exception.php'); 
		throw new Status_Object_Exception('user is already following the other user',29);		
	}
	//We create the link
    $db = self::_getConnexionDbStatic();
	$data = array(
		self::$_dbTimeLineLinksTypeCol => self::$_timelineTypesDbMapping['user'],
		self::$_dbTimeLineLinkstimelineIdCol => $userToFollow->getId(),
		self::$_dbTimeLineLinksUserIdCol => $user->getId()

	);
	$db->insert(self::$_dbTimeLineLinksTableName,$data);
	//We retrun the details of the followed user
	return $userToFollow->asArray();

  }



  
  
    /* create a friendship between the authenticated author and the provided userId */
  static public function deleteFriendship($userId) {
	//We validate the input
	$validator = new Zend_Validate_Digits();
    if(!$validator->isValid($userId)) {
	  	require_once('status/objects/Exception.php');
  	    throw new Status_Object_Exception('Invalid Identifier',15);	
	}
	
	//We retrieve the authenticated user
	$user = Zend_Registry::get('app_user'); 
	
	//we retrieve the user the authenticated user wants to follow
	require_once('Myzen/User.php');

	try{
		$userToUnFollow = new Myzen_User(array('id' => $userId));
	} catch(Zend_Auth_Exception $e) {
		//We convert the zend_auth_exception into a Status_Object_Exception
	  	require_once('status/objects/Exception.php'); 
		throw new Status_Object_Exception($e->getMessage(),$e->getCode());			
	}

	//We check that it is not the same person (can t follow itself)
	if($userToUnFollow->getId() == $user->getId()) {
	  	require_once('status/objects/Exception.php');  
		throw new Status_Object_Exception('user can\'t unfollow him/herself',30);		
	}
	//We check that  the authenticated user is following the user
	if(!self::isFollowing($user, $userToUnFollow)) {
		require_once('status/objects/Exception.php'); 
		throw new Status_Object_Exception('user is not following the other user',31);		
	}
	//We delete
    $db = self::_getConnexionDbStatic();
	$where = array();
	$where[] = $db->quoteInto(self::$_dbTimeLineLinksTypeCol.' = ?',self::$_timelineTypesDbMapping['user']);
	$where[] = $db->quoteInto(self::$_dbTimeLineLinkstimelineIdCol.' = ?',$userToUnFollow->getId());
	$where[] = $db->quoteInto(self::$_dbTimeLineLinksUserIdCol.' = ?',$user->getId());

	$n = $db->delete(self::$_dbTimeLineLinksTableName,$where);
	
	//We return the details of the unfollowed user
	return $userToUnFollow->asArray();

  }








  
  
  //return the number of persones followed and following the provided user
  //format array('nbFollowing' => 12, 'nbFollowers' => 27)
  static public function getConnexionNumbers($userId) {

  	$followingNb = count(self::getFollowingList($userId));
  	$followerNb = count(self::getFollowersList($userId));
  	return array('nbFollowing' => $followingNb, 'nbFollowers' => $followerNb);
  }
  
  
  /* list the different friendship of the user in 2 modes : following or follower
  @param list mode
  */

  static public function listFriendship($mode,$oUser,$perPage,$page) { 	

 	if(!in_array($mode,self::$_allowedFollowTypes)) {
	  	require_once('status/objects/Exception.php');
  	    throw new Status_Object_Exception('Invalid Mode',46);
	}
   	
	$page = self::formatPage($page);
	$perPage = self::formatPerPage($perPage);
 	
 	if(!($oUser instanceof Myzen_User)) {
	  	require_once('status/objects/Exception.php');
  	    throw new Status_Object_Exception('Invalid User',15); 		
 	}
 	
 	$userId = $oUser->getId();
 	//we retrieve the full List
 	if('following' == $mode) $userIdList = self::getFollowingList($userId);
 	elseif('follower' == $mode) $userIdList = self::getFollowersList($userId);
 	else{
	  	require_once('status/objects/Exception.php');
  	    throw new Status_Object_Exception('Invalid Mode',46); 		
 	}
	//we store the results in the data array
	$data = array();

 	//we cut it to the part requested
 	$offset = $perPage * ($page - 1);


 	$listUserPage = array_slice($userIdList, $offset, $perPage);

	if(empty($listUserPage)) return array();

	//Need to retrieve the list of followed people by the authenticated user
	$authUser = Zend_Registry::get('app_user');
	$authUserIdList = self::getFollowingList($authUser->getId());
	//we return the list to get the values first
	$isFollowedList = array_flip($authUserIdList);
	

	require_once('Myzen/User.php');
	foreach($listUserPage as $num => $id) {
		try{
			$user = new Myzen_User(array('id' => $id));
			$tempData['user'] = $user->asArray();
			if(isset($isFollowedList[$id])) $tempData['isFollowed'] = true;
			elseif($id == $authUser->getId()) $tempData['isFollowed'] = 'you'; 
			else $tempData['isFollowed'] = false;
			$data[] = $tempData;
		} catch(Zend_Auth_Exception $e) {
			//we log an error but do nothing
			Zend_Registry::get('dt_log')->log('Issue while retrieving user details for id:'.$id.' as part of '.$mode.' list of user '.$userId, Zend_Log::ERROR);	
		} //end catch
	} //end foreach
 	
 	//we require the data for each user
 	return $data;
 	  
  }
  
  
  
  protected function _getStreamDb() {
  	return self::_getStreamDbStatic($this->_getTimelineCId());
  }

   static protected function _getStreamDbStatic($timelineCId) {
		return Zend_Registry::get('dt_dbs')->getDb('status','timeline',$timelineCId);	
  }
  
   static protected function _getStreamDbNumStatic($timelineCId) {
		return Zend_Registry::get('dt_dbs')->getDbNum('status','timeline',$timelineCId);	
  }

  /*
  This function retrieves all the databases that are used by the stream
  Usefull for deleting the status when you don't know where it could have been stored
  @param void
  @return array of databases
  */
  static protected function _getAllStreamDbs() {
  	return Zend_Registry::get('dt_dbs')->getAllDbs('status','timeline');
  }


   static protected function _getConnexionDbStatic() {
		return Zend_Registry::get('dt_dbs')->getDb('status','connexions','key');	
  }

   static protected function _getTagsCounterDbStatic() {
		return Zend_Registry::get('dt_dbs')->getDb('status','tagsCounter','key');	
  }

  
  static protected function _isValidTag($tag) {
   	if(preg_match(  '/([^a-zA-z0-9_]+)/i'  ,  $tag)) return false;
	return true;
  }

  
  
} //end class